﻿using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace ProjScan.GUIExtensions
{
    //
    // 摘要:
    //     Main Class of the Table Plugin. This contains static functions to draw a table,
    //     from the most basic to the most customizable.
    public static class GUITable
    {
        //
        // 摘要:
        //     Draw a table just from the collection's property. This will create columns for
        //     all the visible members in the elements' class, similar to what Unity would show
        //     in the classic vertical collection display, but as a table instead.
        //
        // 参数:
        //   collectionProperty:
        //     The serialized property of the collection.
        //
        //   tableState:
        //     The Table state.
        //
        // 返回结果:
        //     The updated table state.
        public static GUITableState DrawTable(SerializedProperty collectionProperty, GUITableState tableState)
        {
            List<string> list = new List<string>();
            string text = collectionProperty.propertyPath + ".Array.data[0]";
            foreach (SerializedProperty item in collectionProperty.serializedObject.FindProperty(text))
            {
                string text2 = item.propertyPath.Substring(text.Length + 1);
                if (!text2.Contains("."))
                {
                    list.Add(text2);
                }
            }

            return DrawTable(collectionProperty, list, tableState);
        }

        //
        // 摘要:
        //     Draw a table using just the paths of the properties to display. This will create
        //     columns automatically using the property name as title, and will create PropertyEntry
        //     instances for each element.
        //
        // 参数:
        //   collectionProperty:
        //     The serialized property of the collection.
        //
        //   properties:
        //     The paths (names) of the properties to display.
        //
        //   tableState:
        //     The Table state.
        //
        // 返回结果:
        //     The updated table state.
        public static GUITableState DrawTable(SerializedProperty collectionProperty, List<string> properties, GUITableState tableState)
        {
            List<PropertyColumn> propertyColumns = properties.Select((string prop) => new PropertyColumn(prop, ObjectNames.NicifyVariableName(prop), 100f)).ToList();
            return DrawTable(collectionProperty, propertyColumns, tableState);
        }

        //
        // 摘要:
        //     Draw a table by defining the columns's settings and the path of the corresponding
        //     properties. This will automatically create Property Entries using these paths.
        //
        // 参数:
        //   collectionProperty:
        //     The serialized property of the collection.
        //
        //   propertyColumns:
        //     The Property columns, that contain the columns properties and the corresponding
        //     property path.
        //
        //   tableState:
        //     The Table state.
        //
        // 返回结果:
        //     The updated table state.
        public static GUITableState DrawTable(SerializedProperty collectionProperty, List<PropertyColumn> propertyColumns, GUITableState tableState)
        {
            List<List<TableEntry>> list = new List<List<TableEntry>>();
            for (int i = 0; i < collectionProperty.arraySize; i++)
            {
                List<TableEntry> list2 = new List<TableEntry>();
                foreach (PropertyColumn propertyColumn in propertyColumns)
                {
                    list2.Add(new PropertyEntry(collectionProperty.serializedObject, $"{collectionProperty.propertyPath}.Array.data[{i}].{propertyColumn.propertyName}"));
                }

                list.Add(list2);
            }

            return DrawTable(((IEnumerable<PropertyColumn>)propertyColumns).Select((Func<PropertyColumn, TableColumn>)((PropertyColumn col) => col)).ToList(), list, tableState);
        }

        //
        // 摘要:
        //     Draw a table from the columns' settings, the path for the corresponding properties
        //     and a selector function that takes a SerializedProperty and returns the TableEntry
        //     to put in the corresponding cell.
        //
        // 参数:
        //   collectionProperty:
        //     The serialized property of the collection.
        //
        //   columns:
        //     The Selector Columns.
        //
        //   tableState:
        //     The Table state.
        //
        // 返回结果:
        //     The updated table state.
        public static GUITableState DrawTable(SerializedProperty collectionProperty, List<SelectorColumn> columns, GUITableState tableState)
        {
            List<List<TableEntry>> list = new List<List<TableEntry>>();
            for (int i = 0; i < collectionProperty.arraySize; i++)
            {
                List<TableEntry> list2 = new List<TableEntry>();
                foreach (SelectorColumn column in columns)
                {
                    list2.Add(column.selector(collectionProperty.serializedObject.FindProperty($"{collectionProperty.propertyPath}.Array.data[{i}].{column.propertyName}")));
                }

                list.Add(list2);
            }

            return DrawTable(((IEnumerable<SelectorColumn>)columns).Select((Func<SelectorColumn, TableColumn>)((SelectorColumn col) => col)).ToList(), list, tableState);
        }

        //
        // 摘要:
        //     Draw a table completely manually. Each entry has to be created and given as parameter
        //     in entries.
        //
        // 参数:
        //   columns:
        //     The Columns of the table.
        //
        //   entries:
        //     The Entries as a list of rows.
        //
        //   tableState:
        //     The Table state.
        //
        //   hideHeader:
        //     indicates whether it is necessary to forcefully hide, if true, there is no need
        //     to display the table header, it defaults to false.
        //
        //   scrollView:
        //     ndicates whether it needs to enable the ScrollView. If it is true, it means it
        //     needs to be turned on, default is true.
        //
        //   lineHeight:
        //     used to set the specific height of each line, and it only takes effect when it
        //     is greater than EditorGUIUtility.singleLineHeight.
        //
        // 返回结果:
        //     The updated table state.
        public static GUITableState DrawTable(List<TableColumn> columns, List<List<TableEntry>> entries, GUITableState tableState, bool hideHeader = false, bool scrollView = true, float lineHeight = 0f)
        {
            if (tableState == null)
            {
                tableState = new GUITableState();
            }

            CheckTableState(tableState, columns);
            float singleLineHeight = EditorGUIUtility.singleLineHeight;
            singleLineHeight = ((singleLineHeight > lineHeight) ? singleLineHeight : lineHeight);
            EditorGUILayout.BeginHorizontal();
            if (scrollView)
            {
                tableState.scrollPosHoriz = EditorGUILayout.BeginScrollView(tableState.scrollPosHoriz);
            }

            EditorGUILayout.BeginHorizontal();
            GUILayout.Space(2f);
            float num = 0f;
            RightClickMenu(tableState, columns);
            for (int i = 0; i < columns.Count; i++)
            {
                TableColumn tableColumn = columns[i];
                if (!tableState.columnVisible[i])
                {
                    continue;
                }

                string text = tableColumn.title;
                if (tableState.sortByColumnIndex == i)
                {
                    text = ((!tableState.sortIncreasing) ? (text + " " + '▼') : (text + " " + '▲'));
                }

                ResizeColumn(tableState, i, num);
                GUI.enabled = tableColumn.enabledTitle;
                if (!hideHeader && GUILayout.Button(text, EditorStyles.miniButtonMid, GUILayout.Width(tableState.columnSizes[i] + 4f), GUILayout.Height(EditorGUIUtility.singleLineHeight)) && tableColumn.isSortable)
                {
                    if (tableState.sortByColumnIndex == i && tableState.sortIncreasing)
                    {
                        tableState.sortIncreasing = false;
                    }
                    else if (tableState.sortByColumnIndex == i && !tableState.sortIncreasing)
                    {
                        tableState.sortByColumnIndex = -1;
                    }
                    else
                    {
                        tableState.sortByColumnIndex = i;
                        tableState.sortIncreasing = true;
                    }
                }

                num += tableState.columnSizes[i] + 4f;
            }

            GUI.enabled = true;
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.BeginVertical();
            if (scrollView)
            {
                tableState.scrollPos = EditorGUILayout.BeginScrollView(tableState.scrollPos, GUIStyle.none, GUI.skin.verticalScrollbar);
            }

            List<List<TableEntry>> list = entries;
            if (tableState.sortByColumnIndex >= 0)
            {
                list = ((!tableState.sortIncreasing) ? entries.OrderByDescending((List<TableEntry> row) => row[tableState.sortByColumnIndex]).ToList() : entries.OrderBy((List<TableEntry> row) => row[tableState.sortByColumnIndex]).ToList());
            }

            foreach (List<TableEntry> item in list)
            {
                EditorGUILayout.BeginHorizontal();
                for (int j = 0; j < item.Count; j++)
                {
                    if (j >= columns.Count)
                    {
                        Debug.LogWarning("The number of entries in this row is more than the number of columns");
                    }
                    else if (tableState.columnVisible[j])
                    {
                        TableColumn tableColumn2 = columns[j];
                        TableEntry tableEntry = item[j];
                        GUI.enabled = tableColumn2.enabledEntries;
                        tableEntry.DrawEntry(tableState.columnSizes[j], singleLineHeight);
                    }
                }

                EditorGUILayout.EndHorizontal();
            }

            GUI.enabled = true;
            if (scrollView)
            {
                EditorGUILayout.EndScrollView();
            }

            EditorGUILayout.EndVertical();
            if (scrollView)
            {
                EditorGUILayout.EndScrollView();
            }

            EditorGUILayout.EndHorizontal();
            tableState.Save();
            return tableState;
        }

        private static void RightClickMenu(GUITableState tableState, List<TableColumn> columns)
        {
            Rect rect = new Rect(0f, 0f, tableState.columnSizes.Where((float _, int i) => tableState.columnVisible[i]).Sum((float s) => s + 4f), EditorGUIUtility.singleLineHeight);
            GUI.enabled = true;
            if (!rect.Contains(Event.current.mousePosition) || Event.current.type != 0 || Event.current.button != 1)
            {
                return;
            }

            GenericMenu genericMenu = new GenericMenu();
            for (int j = 0; j < columns.Count; j++)
            {
                TableColumn tableColumn = columns[j];
                if (tableColumn.optional)
                {
                    int index = j;
                    genericMenu.AddItem(new GUIContent(tableColumn.title), tableState.columnVisible[j], delegate
                    {
                        tableState.columnVisible[index] = !tableState.columnVisible[index];
                    });
                }
            }

            genericMenu.ShowAsContext();
        }

        private static void ResizeColumn(GUITableState tableState, int indexColumn, float currentX)
        {
            int controlID = GUIUtility.GetControlID(FocusType.Passive);
            Rect position = new Rect(currentX + tableState.columnSizes[indexColumn] + 2f, 0f, 10f, EditorGUIUtility.singleLineHeight);
            EditorGUIUtility.AddCursorRect(position, MouseCursor.ResizeHorizontal, controlID);
            switch (Event.current.type)
            {
                case EventType.MouseMove:
                    break;
                case EventType.MouseDown:
                    if (position.Contains(Event.current.mousePosition))
                    {
                        GUIUtility.hotControl = controlID;
                        Event.current.Use();
                    }

                    break;
                case EventType.MouseDrag:
                    if (GUIUtility.hotControl == controlID)
                    {
                        tableState.columnSizes[indexColumn] = Event.current.mousePosition.x - currentX - 5f;
                        Event.current.Use();
                    }

                    break;
                case EventType.MouseUp:
                    if (GUIUtility.hotControl == controlID)
                    {
                        GUIUtility.hotControl = 0;
                        Event.current.Use();
                    }

                    break;
            }
        }

        private static void CheckTableState(GUITableState tableState, List<TableColumn> columns)
        {
            if (tableState.columnSizes == null || tableState.columnSizes.Count < columns.Count)
            {
                tableState.columnSizes = columns.Select((TableColumn column) => column.width).ToList();
            }

            if (tableState.columnVisible == null || tableState.columnVisible.Count < columns.Count)
            {
                tableState.columnVisible = columns.Select((TableColumn column) => column.visibleByDefault).ToList();
            }
        }
    }
}